/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include "SQLiteDB.h"
#include "Logger.h"

SQLiteDB::SQLiteDB():
	mErrMsg(NULL),
	mDBResult(NULL),
	mDBResultValid(false),
	mRowCount(0),
	mColumnCount(0),
	mQueryResult(0)
{
}

SQLiteDB::~SQLiteDB()
{
	for (	map<QString, sqlite3*>::iterator db_it = mDBs.begin();
			db_it != mDBs.end();
			db_it++)
	{
		LOG<<"Close database: "<<db_it->first.toUtf8().data()<<endl;
		sqlite3_close( db_it->second );
	}
}

bool SQLiteDB::openDB(QString fileName, QString dbName, bool create)
{
	if (!QFile::exists(fileName) && !create)
	{
		LOG<<"File \""<<fileName.toUtf8().data()<<"\"  does not exist."<<endl;
		return false;
	}

	map<QString, sqlite3*>::iterator db_it = mDBs.find(dbName);
	if (db_it != mDBs.end())
	{
		LOG<<"Cannot open database: "<<dbName.toUtf8().data()<<". Each database cannot be opened more than once."<<endl;
		LOG<<"File=\""<<QString(fileName).toUtf8().data()<<"\""<<endl;
		return false;
	}

	//LOG<<"dbFile.absolutePath()="<<dbFile.absolutePath().toUtf8().data()<<endl;
	mQueryResult = sqlite3_open( fileName.toUtf8().data(), &(mDBs[dbName]));
	if( mQueryResult == SQLITE_OK )
	{
		//LOG<<"dbFileName="<<dbFileName.toUtf8().data()<<endl;
		return true;
	}
	else
	{
		LOG<<"Failed to open database: "<<dbName.toUtf8().data()<<"\terror code ="<<mQueryResult<<endl;
		return false;
	}
}

void SQLiteDB::closeDB(QString dbName)
{
	map<QString, sqlite3*>::iterator db_it = mDBs.find(dbName);
	if (db_it != mDBs.end())
	{
		sqlite3_close(db_it->second);
		mDBs.erase(db_it);
	}
	else
	{
		LOG<<"Fail to close database dbName: "<<dbName.toUtf8().data()<<endl;
		assert(0);
	}
}

bool SQLiteDB::hasDB(QString dbName)
{
	map<QString, sqlite3*>::iterator db_it = mDBs.find(dbName);
	if (db_it == mDBs.end())
		return false;
	else
		return true;
}

bool SQLiteDB::execQuery(QString command, QueryType queryType , QString dbName)
{
	QMutexLocker locker(&mQMutex);

	mDBResultValid = false;
	if (queryType == QueryT_Last)
		queryType = mLastQueryType;

	if (dbName.isEmpty())
		dbName = mLastQueryDB;

	map<QString, sqlite3*>::iterator db_it = mDBs.find(dbName);
	if (db_it == mDBs.end())
	{
		LOG<<"Invalid database: "<<dbName.toUtf8().data()<<endl;
		assert(0);
	}

	if ( queryType == QueryT_Write)
	{
		mQueryResult = sqlite3_exec( mDBs[dbName], command.toUtf8().data(), NULL, NULL, &mErrMsg );
		if( mQueryResult != SQLITE_OK )
		{
			LOG<<"Write operation failed. "<<endl;
			LOG<<"Command = "<<command.toUtf8().data()<<endl;
			LOG<<"Error Message = "<<mErrMsg<<endl;
			LOG<<"dbName="<<dbName.toUtf8().data()<<endl;

			assert(0);
		}
		else
		{
			mLastQueryType = QueryT_Write;
			if (!dbName.isEmpty())
				mLastQueryDB = dbName;
			mDBResultValid = false;
			return true;
		}
	}
	else if ( queryType == QueryT_Read)
	{
		if (mDBResult)
		{
			sqlite3_free_table( mDBResult );
			mDBResult = NULL;
		}

		mQueryResult = sqlite3_get_table( mDBs[dbName], command.toUtf8().data(),
				&mDBResult, &mRowCount, &mColumnCount, &mErrMsg );
		if( mQueryResult != SQLITE_OK )
		{
			LOG<<"Read operation failed. "<<endl;
			LOG<<"Command = "<<command.toUtf8().data()<<endl;
			LOG<<"Error Message = "<<mErrMsg<<endl;
			LOG<<"dbName="<<dbName.toUtf8().data()<<endl;

			assert(0);
		}
		else
		{
			mLastQueryType = QueryT_Read;
			if (!dbName.isEmpty())
				mLastQueryDB = dbName;
			mDBResultValid = true;
			return true;
		}
	}
}

int SQLiteDB::updateBlob(QString command, QByteArray buffer, QString dbName)
{
	map<QString, sqlite3*>::iterator db_it = mDBs.find(dbName);
	if (db_it == mDBs.end())
	{
		LOG<<"Invalid database: "<<dbName.toUtf8().data()<<endl;
		assert(0);
	}

	sqlite3_stmt  *stat;
	sqlite3_prepare(mDBs[dbName], command.toUtf8().data(), -1, &stat, 0 );
	//LOG<<"SQLiteDB::quickUpdateBlob->prepare_result="<<prepare_result<<endl;
	sqlite3_bind_blob( stat, 1, buffer.data(), buffer.length(), NULL );
	//LOG<<"SQLiteDB::quickUpdateBlob->bind_result="<<bind_result<<endl;
	int result=sqlite3_step( stat );
	sqlite3_finalize( stat );
	return result;
}

QString SQLiteDB::getTableValue(int row, int column)
{
	if (mColumnCount==0)
	{
		LOG<<"Empty result"<<endl;
		return QString();
	}
	//LOG<<"mDBResult="<<mDBResult[mColumnCount+row*mColumnCount+column]<<endl;
	if (row >= mRowCount || column >= mColumnCount || row < 0 || column < 0)
	{
		LOG<<"Out of rage ("<<row<<","<<column<<")"<<endl;
		assert(0);
	}

	return QString::fromUtf8(mDBResult[mColumnCount+row*mColumnCount+column]);
}

QString SQLiteDB::getTableValue(int row, QString columnName)
{
	//LOG<<"SQLiteDB::getTableValue("<<row<<", "<<columnName.toUtf8().data()<<")"<<endl;
	if (mColumnCount==0)
	{
		LOG<<"Empty result"<<endl;
		return QString();
	}

	int column = -1;
	for (int i=0; i<mColumnCount; i++)
	{
		if (QString(mDBResult[i]) == columnName)
		{
			column = i;
			break;
		}
	}

	if (column == -1)
	{
		LOG<<"Invalid column:"<<column<<"\tcolumnName="<<columnName.toUtf8().data()<<endl;
		LOG<<"Valid column names are: "<<endl;
		for (int i=0; i<mColumnCount; i++)
			LOG<<mDBResult[i]<<endl;
		assert(0);
	}
	else
		return getTableValue(row, column);
}
QByteArray SQLiteDB::getSingleBlob(QString command, QString dbName)
{
	map<QString, sqlite3*>::iterator db_it = mDBs.find(dbName);
	if (db_it == mDBs.end())
	{
		LOG<<"Invalid database: "<<dbName.toUtf8().data()<<endl;
		assert(0);
	}

	sqlite3_stmt * stat;
	int prepare_result = sqlite3_prepare( mDBs[dbName], command.toUtf8().data() , -1, &stat, 0 );
	assert(prepare_result == SQLITE_OK);
	int step_result = sqlite3_step( stat );
	if (step_result == SQLITE_DONE)
	{
		LOG<<"Empty result"<<endl;
		return QByteArray();
	}

	//LOG<<"step_result="<<step_result<<endl;
	assert(step_result == SQLITE_ROW );

	const void * pFileContent = sqlite3_column_blob( stat, 0 );
	int len = sqlite3_column_bytes( stat, 0 );
	sqlite3_finalize( stat );
	LOG<<QByteArray((char*)pFileContent, len).toHex().data()<<endl;
	return QByteArray((char*)pFileContent, len);
}

QString SQLiteDB::getColName(int col)
{
	return QString(mDBResult[col]);
}

void SQLiteDB::createEmptyTable(QString dbName, QString tableName, QStringList colNames)
{
	QString command;
	command = QString("drop table if exists %1;").arg(tableName);
	execQuery(command, QueryT_Write, dbName);

	command.clear();
	command.append(QString("create table %1 ( ").arg(tableName));
	for (int i=0; i<colNames.size()-1; i++)
	{
		command.append(colNames[i]);
		command.append(", ");
	}
	command.append(colNames[colNames.size()-1]);
	command.append(" );");
	execQuery(command, QueryT_Write, dbName);
}

